import { startAnim, setPropertyValue, isArray, isEmpty } from "./util";
import { STYLE } from "./constant";

export default class Anim {
    constructor(dm) {
        this.dm = dm;
        this.defaultDuration = 1000;
        this.anims = [];
    }
    
    start() {

    }
    // 停止并清空
    clear() {
        this.anims.forEach(anim => {
            anim.pause();
        });
        this.anims = [];
    }
    // 所有动画停止pause
    stopAll() {
        this.anims.forEach(anim => {
            anim.stop();
        });
    }
    // 开启动画函数
    startAnim(option) {
        let optionFinishFunc = option.finishFunc;
        let anim;
        option.finishFunc = () => {
            optionFinishFunc && optionFinishFunc();
            this.anims.splice(this.anims.indexOf(anim), 1);
        }
        anim = startAnim(option);
        this.anims.push(anim);
        return anim;
    }
    // 根据节点类型设置数值
    setValue(data, accessType, name, value) {
        // 判断节点所属的类型，根据所属的类型来设定相应属性的值
        if (data instanceof ht.Data) {
            setPropertyValue(data, accessType, name, value);
        }
        else if (data instanceof ht.List) {
            data.each(d => {
                setPropertyValue(d, accessType, name, value);
            });
        }
        else if (isArray(data)) {
            data.forEach(d => {
                setPropertyValue(d, accessType, name, value);
            });
        }
    }


    /**
     * 淡入
     * @param {*} node 
     * @param {*} duration 
     */
    fadeIn(data, easing, duration = this.defaultDuration) {
        return new Promise((resolve) => {
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, STYLE, 'opacity', v);
                }
            });
        });
    }
    /**
     * 淡出
     * @param {*} data 
     * @param {*} duration 
     */
    fadeOut(data, easing, duration = this.defaultDuration) {
        return new Promise((resolve) => {
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, STYLE, 'opacity', 1 - v);
                }
            });
        });
    }
    // 裁剪过渡出现动画
    clipIn(data, easing, duration = this.defaultDuration) {
        return new Promise((resolve) => {
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    // 根据节点属性值参数调用设定动画值的方法
                    this.setValue(data, STYLE, 'clip.percentage', v);
                }
            });
        });
    }
    // 裁剪过渡消失动画
    clipOut(data, easing, duration = this.defaultDuration) {
        return new Promise((resolve) => {
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, STYLE, 'clip.percentage', 1 - v);
                }
            });
        });
    }
    // 放大过渡出现动画
    scaleIn(data, easing, duration = this.defaultDuration) {
        return new Promise((resolve) => {
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, null, 'scaleX', v);
                    this.setValue(data, null, 'scaleY', v);
                }
            });
        });
    }
    // 放大过渡消失动画
    scaleOut(data, easing, duration = this.defaultDuration) {
        return new Promise((resolve) => {
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, null, 'scaleX', 1 - v);
                    this.setValue(data, null, 'scaleY', 1 - v);
                }
            });
        });
    }
    // 数值过渡跳动增减动画
    fromTo(data, accessType, name, from, to, easing, duration = this.defaultDuration) {
        return new Promise(resolve => {
            let range = to - from;
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, accessType, name, from + v * range);
                }
            });
        });
    }
    // 整数（int）数值过渡跳动增减动画
    intFromTo(data, accessType, name, from, to, easing, duration = this.defaultDuration) {
        return new Promise(resolve => {
            let range = to - from;
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, accessType, name, parseInt(from + v * range));
                }
            });
        });
    }
    // 浮点型（float）数值过渡跳动增减动画
    floatFromTo(data, accessType, name, from, to, easing, duration = this.defaultDuration, fixed = 1) {
        return new Promise(resolve => {
            let range = to - from;
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, accessType, name, 1 * ((from + v * range).toFixed(fixed)));
                }
            });
        });
    }
    // 整数数组（int Array）数值过渡跳动增减动画
    intArrayFromTo(data, accessType, name, fromArray, toArray, easing, duration) {
        return new Promise(resolve => {
            let rangeArray = fromArray.map((item, i) => {
                return toArray[i] - item;
            });
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    let newData = fromArray.map((item, i) => {
                        return parseInt(item + rangeArray[i] * v);
                    });
                    this.setValue(data, accessType, name, newData);
                }
            });
        });
    }
    // 平移动画
    move(data, from, to, easing, duration = this.defaultDuration) {
        return new Promise(resolve => {
            let rangeX = to.x - from.x,
                rangeY = to.y - from.y;
            this.startAnim({
                duration,
                easing,
                finishFunc: () => {
                    resolve();
                },
                action: v => {
                    this.setValue(data, null, 'x', from.x + v * rangeX);
                    this.setValue(data, null, 'y', from.y + v * rangeY);
                }
            });
        });
    }
    // 随机整数取值
    randomInt(min, max) {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        let range = max - min;
        return parseInt(Math.random() * range) + min;
    }
    /**
     * 旋转动画
     * @param {*} data 
     * @param {*} step 
     * @param {*} interval 
     */
    keepRotation(data, step, interval = 50) {
        let currentRotation = 0,
            lastTime = new Date().getTime();
        return this.startAnim({
            frames: Infinity,
            interval,
            action: () => {
                let time = new Date().getTime(),
                    deltaTime = time - lastTime,
                    offset = step * (deltaTime / interval);
                lastTime = time;
                currentRotation += offset;
                this.setValue(data, null, 'rotation', currentRotation);
            }
        });
    }
    /**
     * 旋转动画
     * @param {*} data 
     * @param {*} step 
     * @param {*} interval 
     */
    keepRotationZ(data, step, interval = 50) {
        let currentRotation = 0,
            lastTime = new Date().getTime();
        return this.startAnim({
            frames: Infinity,
            interval,
            action: () => {
                let time = new Date().getTime(),
                    deltaTime = time - lastTime,
                    offset = step * (deltaTime / interval);
                lastTime = time;
                currentRotation += offset;
                this.setValue(data, null, 'rotationZ', currentRotation);
            }
        });
    }
    /**
     * 三维y旋转动画
     * @param {*} data 
     * @param {*} step 
     * @param {*} interval 
     */
    keepRotationY(data, step, interval = 50) {
        let currentRotation = 0,
            lastTime = new Date().getTime();
        return this.startAnim({
            frames: Infinity,
            interval,
            action: () => {
                let time = new Date().getTime(),
                    deltaTime = time - lastTime,
                    offset = step * (deltaTime / interval);
                lastTime = time;
                currentRotation += offset;
                this.setValue(data, null, 'rotationY', currentRotation);
            }
        });
    }
    /**
     * 三维x旋转动画
     * @param {*} data 
     * @param {*} step 
     * @param {*} interval 
     */
    keepRotationX(data, step, interval = 50) {
        let currentRotation = 0,
            lastTime = new Date().getTime();
        return this.startAnim({
            frames: Infinity,
            interval,
            action: () => {
                let time = new Date().getTime(),
                    deltaTime = time - lastTime,
                    offset = step * (deltaTime / interval);
                lastTime = time;
                currentRotation += offset;
                this.setValue(data, null, 'rotationX', currentRotation);
            }
        });
    }

    // 在y轴方向以正弦曲线浮动
    keepFloat(data, fixY, offset = 10) {
        let v = Math.random();
        if (isEmpty(fixY)) {
            fixY = data.getElevation();
        }
        return this.startAnim({
            frames: Infinity,
            interval: 50,
            action: () => {
                data && data.setElevation(Math.sin(Math.PI * 2 * v) * offset + fixY);
                v += 0.01;
            }
        });
    }
    keepUOffset(data, step, interval = 50) {
        return this.startAnim({
            frames: Infinity,
            interval,
            action: () => {
                let uv = (data.s('shape3d.uv.offset') || [0, 0]).slice(0);
                uv[0] += step;
                data.s('shape3d.uv.offset', uv);
            }
        });
    }
    keepTopUOffset(data, step, interval = 50) {
        return this.startAnim({
            frames: Infinity,
            interval,
            action: () => {
                let uv = (data.s('top.uv.offset') || [0, 0]).slice(0);
                uv[0] += step;
                data.s('top.uv.offset', uv);
            }
        });
    }

    keepRingsOut(list) {
        return this.startAnim({
            frames: Infinity,
            interval: 60,
            action: () => {
                list.forEach(ring => {
                    let r = ring.getRotationY() || 0,
                        scale = ring.getScaleX(),
                        opacity = ring.s('shape3d.opacity') || 0;

                    scale += 0.1;
                    opacity = 1 - scale / 4 * 0.75;

                    scale = scale > 4 ? 0 : scale;
                    opacity = opacity > 1 ? 0 : opacity;
                    
                    ring.s('shape3d.opacity', opacity);
                    ring.setScale(scale, scale);
                    ring.setRotationY(r - Math.PI / 20);
                });
            }
        });
    }

    keep3dOpacityPingpong(list) {
        return this.startAnim({
            frames: Infinity,
            interval: 60,
            action: () => {
                list.forEach(ring => {
                    let opacity = ring.s('shape3d.opacity') || 0;

                    if (ring._isSub) {
                        opacity -= 0.02;
                        if (opacity < 0) {
                            opacity = 0;
                            ring._isSub = false;
                        }
                    }
                    else {
                        opacity += 0.08;
                        if (opacity > 1) {
                            ring._isSub = true;
                        }
                    }
                    ring.s('shape3d.opacity', opacity);
                });
            }
        });
    }
    pipeRoam(pipe, view, finishFunc, duration = 5000) {
        return new Promise((resolve) => {
            let length = view.getLineLength(pipe);
            this.startAnim({
                duration,
                finishFunc,
                action: (v) => {
                    let offset = view.getLineOffset(pipe, length * v),
                        p = offset.point;

                    view.setEye([p.x, p.y, p.z]);
                }
            });
        });
    }

    /**
     * 3D 飞光，以一个节点为模板，在指定区域随机出现不断从下往上升起到消失
     * @param {*} grainTemp 模板节点，即单个飞光的节点
     * @param {*} area 三维区域，由 g3d.getBoundingBox(node) 获得的对象 { min: [x, y, z], max: [x, y, z] }
     * @param {*} speed 速度，倍数，比如 2 是原速度的两倍
     * @param {*} max 最大展示数量，值越大，越密集
     */
    keepGrainFly(grainTemp, boundingBox, speed = 1, maxCount = 20) {
        let pool = [],
            { min, max} = boundingBox,
            [ minX, minY, minZ] = min,
            [ maxX, maxY, maxZ ] = max;

        for (let i = 0; i < maxCount; i++) {
            let grain = new ht.Node();
            grain.setStyleMap(clone(grainTemp.getStyleMap()));
            grain.setAttrObject(clone(grainTemp.getAttrObject()));
            grain.s3(grainTemp.s3());
            grain.setScale3d(grainTemp.getScale3d());
            grain.setAnchor3d(grainTemp.getAnchor3d());
            grain.s('3d.visible', false);
            this.dm.add(grain);
            pool.push(grain);
        }

        return this.startAnim({
            frames: Infinity,
            interval: 60,
            action: () => {
                pool.forEach(gn => {
                    if (!gn.s('3d.visible') && Math.random() < 0.08) {
                        gn.s('3d.visible', true);
                        gn.a('step', this.randomInt(5, 15) * speed);
                        gn.s('shape3d.opacity', Math.random());
                        gn.p3([this.randomInt(minX, maxX), minY, this.randomInt(minZ, maxZ)]);
                        return;
                    }

                    if (gn.s('3d.visible')) {
                        let step = gn.a('step'),
                            elevation = gn.getElevation() + step;

                        gn.setElevation(elevation);
                        if (elevation > maxY) {
                            gn.s('3d.visible', false);
                        }
                    }
                });
            }
        });

    }

}
